package com.gitee.qdbp.jdbc.result;

import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.LinkedHashMap;
import java.util.Map;
import org.springframework.jdbc.support.JdbcUtils;
import com.gitee.qdbp.able.exception.ServiceException;
import com.gitee.qdbp.jdbc.exception.DbErrorCode;
import com.gitee.qdbp.jdbc.model.AllFieldColumn;
import com.gitee.qdbp.jdbc.model.FieldColumns;
import com.gitee.qdbp.jdbc.model.FieldScene;
import com.gitee.qdbp.jdbc.model.SimpleFieldColumn;
import com.gitee.qdbp.jdbc.plugins.DbPluginHelper;
import com.gitee.qdbp.jdbc.plugins.TablesFieldColumnParser;
import com.gitee.qdbp.tools.utils.StringTools;

/**
 * 单表查询结果集行数据到JavaBean的转换处理类<br>
 * ResultSet是列名与字段值的对应关系, 需要转换为字段名与字段值的对应关系
 *
 * @author zhaohuihua
 * @version 190617
 */
public class TableRowToBeanMapper<T> implements RowToBeanMapper<T>, DbPluginHelper.Aware {

    private final Class<T> mappedClass;
    /** 插件容器 **/
    private DbPluginHelper plugins;

    public TableRowToBeanMapper(Class<T> mappedClass) {
        this.mappedClass = mappedClass;
    }

    protected final Class<T> getMappedClass() {
        return this.mappedClass;
    }

    @Override
    public void setPlugins(DbPluginHelper plugins) {
        this.plugins = plugins;
    }

    @Override
    public T mapRow(ResultSet rs, int rowNum) throws SQLException {
        // 将ResultSet的行数据转换为Map
        Map<String, Object> map = rowToMap(rs, rowNum);
        // 利用工具类进行Map到JavaBean的转换
        return plugins.getMapToBeanConverter().convert(map, getMappedClass());
    }

    protected Map<String, Object> rowToMap(ResultSet rs, int rowNum) throws SQLException {
        // 获取结果类的所有字段信息
        AllFieldColumn<SimpleFieldColumn> all = plugins.parseAllFieldColumns(getMappedClass());
        FieldColumns<SimpleFieldColumn> fieldColumns = all.filter(FieldScene.RESULT);
        ResultSetMetaData rsmd = rs.getMetaData();
        int columnCount = rsmd.getColumnCount();
        Map<String, Object> mapOfColumnValues = new LinkedHashMap<>(columnCount);

        Integer lastIndex = null;
        SimpleFieldColumn lastField = null;
        try {
            for (int i = 1; i <= columnCount; i++) {
                lastIndex = null;
                lastField = null;
                String columnName = getColumnName(JdbcUtils.lookupColumnName(rsmd, i));
                // 根据列名查找字段信息
                SimpleFieldColumn field = fieldColumns.findByColumnAlias(columnName);
                if (field != null) {
                    lastIndex = i;
                    lastField = field;
                    fillColumnValue(rs, i, field, mapOfColumnValues);
                }
            }
        } catch (Exception e) {
            // try catch 放在循环外面, 提高性能
            if (lastField == null) {
                throw e;
            } else {
                Object lastValue = JdbcUtils.getResultSetValue(rs, lastIndex);
                String details = String.format("%s.%s, FieldType:%s, ColumnName:%s, ColumnType:%s, ColumnValue:%s",
                        mappedClass.getSimpleName(), lastField.getFieldName(),
                        lastField.getJavaType().getSimpleName(), lastField.getColumnName(),
                        lastValue.getClass().getSimpleName(), StringTools.ellipsis(lastValue.toString(), 100));
                throw new ServiceException(DbErrorCode.DB_COLUMN_VALUE_ERROR, details, e);
            }
        }
        return mapOfColumnValues;
    }

    protected String getColumnName(String columnName) {
        TablesFieldColumnParser parser = plugins.getTablesFieldColumnParser();
        // 表别名与列别名的分隔符
        String tableAliasSeparator = parser.getTableAliasSeparator();
        int tableAliasIndex = columnName.indexOf(tableAliasSeparator);
        if (tableAliasIndex >= 0) {
            columnName = columnName.substring(tableAliasIndex + tableAliasSeparator.length());
        }
        return columnName;
    }

    protected void fillColumnValue(ResultSet rs, int index, SimpleFieldColumn field, Map<String, Object> maps)
            throws SQLException {
        String fieldName = field.getFieldName();
        Class<?> fieldType = field.getJavaType();
        Object value = getColumnValue(rs, index, fieldType);
        maps.put(fieldName, value);
    }

    protected Object getColumnValue(ResultSet rs, int index, Class<?> fieldType) throws SQLException {
        return JdbcUtils.getResultSetValue(rs, index, fieldType);
    }

    /**
     * TableRowToBeanMapper的工厂类
     *
     * @author zhaohuihua
     * @version 20210306
     * @serial 3.3.0
     */
    public static class Factory implements FactoryOfTable, DbPluginHelper.Aware {

        /** 插件容器 **/
        private DbPluginHelper plugins;

        /** 插件容器 **/
        @Override
        public void setPlugins(DbPluginHelper plugins) {
            this.plugins = plugins;
        }

        @Override
        public <T> RowToBeanMapper<T> getRowToBeanMapper(Class<T> mappedClass) {
            TableRowToBeanMapper<T> mapper = new TableRowToBeanMapper<>(mappedClass);
            mapper.setPlugins(plugins);
            return mapper;
        }
    }
}
